﻿/*SOWY Date Operations class
 * Author: WANG JIAN
 * Group: f10-2s
 * Version 0.01
 * This class is to deal mainly with the text based database
 * Basic function include adding of data, retrieval of data, modify and deleting of data.
 * This class may include ability to record a log of user input history in the future
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using SOWY.common;
using System.Data.Odbc;
using System.Data;

namespace SOWY
{
    class DataOperations
    {
        private string fileLoc;
        private string DATABASE_CONN_STRING;

        #region Database initialization
        public DataOperations(string fileLocation = GeneralConstant.DATABASE_FOLDER+GeneralConstant.DATABASE_FILE)
        {
            fileLoc = fileLocation;
            if (!isFolderExist(GeneralConstant.DATABASE_FOLDER))
            {
                Directory.CreateDirectory(GeneralConstant.DATABASE_FOLDER);
            }
            if (!isFileExist(fileLoc))
            {
                using (FileStream fs = File.Create(fileLoc, 1024))
                {
                    Byte[] info = new UTF8Encoding(true).GetBytes("");
                    fs.Write(info, 0, info.Length);
                    fs.Close();
                }
            }
            initiateLogFile();
        }

        private bool isFolderExist(string path)
        {
            if (Directory.Exists(path))
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        private bool isFileExist(string location)
        {
            if (File.Exists(location))
            {
                return true;
            }
            return false;
        }
        #endregion

        #region Command processing
        public List<Task> toDatabase(string CRUD, string task, DateTime firstDate, DateTime secondDate, string specialKey = null, string dateTypeKeywords = null)
        {
            List<Task> dataBase = readTasks();
            List<Task> returnTasks = new List<Task>();

            if (CRUD == GeneralConstant.KEY_ADD)
            {
                returnTasks = addTask(task, firstDate, secondDate, dataBase);
            }

            if (CRUD == GeneralConstant.KEY_RETRIEVE)
            {
                returnTasks = dataBase;
            }


            if (CRUD == GeneralConstant.KEY_DELETE)
            {
                returnTasks = deleteTask(task, firstDate, secondDate, dataBase);
            }

            if (CRUD == GeneralConstant.KEY_UPDATE)
            {
                returnTasks = updateTask(task, firstDate, secondDate, dataBase, specialKey);
            }
            returnTasks.Sort((x, y) => DateTime.Compare(x.startDate, y.startDate));
            returnTasks.Sort((x, y) => DateTime.Compare(x.endDate, y.endDate));
            return returnTasks;
        }
        #endregion

        #region Command operations
        private List<Task> updateTask(string task, DateTime firstDate, DateTime secondDate, List<Task> dataBase, string specialKey)
        {
            int tasksToAdd = 1;
            String[] split = new String[] { };
            List<Task> returnTasks = new List<Task>();
            List<Task> deletedTasks = new List<Task>();
            split = task.Split('|');

            if (split.Count() < 1)//Change task.
            {
                split[GeneralConstant.FIRST_ITEM] = task;
                split[1] = task;
            }

            {
                if (specialKey != null)
                {
                    deletedTasks = deleteTask(split[0], firstDate, secondDate, dataBase);
                    while ((deletedTasks.Count - tasksToAdd) >= 0)
                    {
                        returnTasks.Add(deletedTasks[deletedTasks.Count - tasksToAdd]);
                        returnTasks.Add((addTask(split[1], returnTasks.ElementAt(returnTasks.Count - tasksToAdd).startDate, DateTime.Parse(specialKey), dataBase))[GeneralConstant.FIRST_ITEM]);
                        tasksToAdd++;
                    }
                }
                else
                {
                    deletedTasks = deleteTask(split[0], firstDate, secondDate, dataBase);
                    while ((deletedTasks.Count - tasksToAdd) >= 0)
                    {
                        returnTasks.Add(deletedTasks[deletedTasks.Count - tasksToAdd]);
                        returnTasks.Add((addTask(split[1], returnTasks.ElementAt(returnTasks.Count - tasksToAdd).startDate, returnTasks.ElementAt(returnTasks.Count - tasksToAdd).endDate, dataBase))[GeneralConstant.FIRST_ITEM]);
                        tasksToAdd++;
                    }
                }
            }

            return returnTasks;
        }

        private List<Task> deleteTask(string task, DateTime firstDate, DateTime secondDate, List<Task> dataBase)
        {
            int allDelete = GeneralConstant.FIRST_ITEM;
            int amountToDelete = GeneralConstant.FIRST_ITEM;
            String[] split = new String[] { };
            List<Task> returnTasks = new List<Task>();
            List<int> deleteTasks = new List<int>();
            int startCount, endCount = GeneralConstant.FIRST_ITEM;

            if (task == GeneralConstant.KEY_ALL)
                while ((dataBase.Count - allDelete) > 0) deleteTasks.Add(allDelete++);
            else
                deleteTasks = searchTasks(dataBase, task, firstDate, secondDate);
            startCount = deleteTasks.Count;

            if (task != null && task != GeneralConstant.KEY_ALL && task.Contains("|"))
            {
                split = task.Split('|');
                amountToDelete = int.Parse(split[1]);
                 if(split[GeneralConstant.FIRST_ITEM] == GeneralConstant.KEY_FIRST)
                 {
                     if((deleteTasks.Count.CompareTo(amountToDelete))<0)
                        startCount = deleteTasks.Count;
                     else
                        startCount = amountToDelete;

                    endCount = GeneralConstant.FIRST_ITEM;
                }
                 else if(split[GeneralConstant.FIRST_ITEM] == GeneralConstant.KEY_LAST)
                 {
                    startCount = deleteTasks.Count;
                    endCount = deleteTasks.Count - amountToDelete - 1;
                 }
            }

            for (int i = startCount - 1; i >= endCount && i >= GeneralConstant.FIRST_ITEM; i--)
            {
                int fileTask = deleteTasks.ElementAt(i);

                dataBase.ElementAt(fileTask).type = GeneralConstant.KEY_DELETE;
                returnTasks.Add(dataBase.ElementAt(fileTask));
                dataBase.RemoveAt(fileTask);
            }
            writeTasks(dataBase);
            return returnTasks;
        }

        private List<Task> addTask(string task, DateTime firstDate, DateTime secondDate, List<Task> dataBase)
        {
            List<Task> returnTasks = new List<Task>();

            Task taskToAdd = new Task(task, firstDate, secondDate, GeneralConstant.KEY_ADD);
            dataBase.Add(taskToAdd);
            if (writeTasks(dataBase))
                returnTasks.Add(taskToAdd);
            else
            {
                taskToAdd.type = GeneralConstant.KEY_ERROR;
                returnTasks.Add(taskToAdd);
            }
            return returnTasks;
        }
        #endregion

        #region Database interactions
        private bool writeTasks(List<Task> task)
        {
            string textToWrite = null;
            foreach (Task eachTask in task)
                textToWrite += eachTask.print();
            try
            {
                File.WriteAllText(fileLoc, textToWrite);
            }
            catch
            {
                return false;
            }
            return true;
        }
        private List<Task> readTasks()
        {
            List<Task> allTasks = new List<Task>();

            List<string> lines = new List<string>();
            try
            {
                //lines = File.ReadAllLines(fileLoc).ToList();
                // Read in every line in the file.
                using (StreamReader reader = new StreamReader(fileLoc))
                {
                    string line;
                    while ((line = reader.ReadLine()) != null)
                    {
                        // Do something with line
                        string[] split = line.Split('|');

                        Task task = new Task();
                        task.startDate = Convert.ToDateTime(split[1]);
                        task.endDate = Convert.ToDateTime(split[2]);
                        task.task = split[0];

                        allTasks.Add(task);
                    }
                }
            }
            catch (IOException ex)
            {
                lines.Add(ex.ToString());
            }
            return allTasks;
        }
        private List<int> searchTasks(List<Task> textLines, string job, DateTime searchStartDate, DateTime searchEndDate)
        {
            List<int> searchedStrings = new List<int>();
            for (int noOfLines = GeneralConstant.FIRST_ITEM; noOfLines < textLines.Count; noOfLines++)
            {
                if (searchStartDate == DateTime.MinValue && searchEndDate == DateTime.MinValue)
                {
                    if (textLines[noOfLines].task == job)
                        searchedStrings.Add(noOfLines);
                }
                else
                {
                    if (textLines[noOfLines].task == job && textLines[noOfLines].startDate >= searchStartDate && textLines[noOfLines].endDate <= searchEndDate)
                        searchedStrings.Add(noOfLines);
                }
            }
            return searchedStrings;
        }
        #endregion

        /* Logging operations*/
        #region Logging
        private void initiateLogFile()
        {
            if (!File.Exists(GeneralConstant.DATABASE_FOLDER + GeneralConstant.DATABASE_CSV_LOG_FILE))
            {
                using (FileStream fs = File.Create(GeneralConstant.DATABASE_FOLDER + GeneralConstant.DATABASE_CSV_LOG_FILE, 1024))
                {
                    Byte[] info = new UTF8Encoding(true).GetBytes("action,startDate,endDate,task");
                    fs.Write(info, 0, info.Length);
                    fs.Close();
                }
            }
            DATABASE_CONN_STRING = "Driver={Microsoft Text Driver (*.txt; *.csv)};Dbq=" + GeneralConstant.DATABASE_FOLDER + ";Extensions=asc,csv,tab,txt;";
        }

        public void insertLog(string action, string task, string startDate, string endDate)
        {
            OdbcConnection conn = new OdbcConnection(DATABASE_CONN_STRING);
            conn.Open();
            string sql = "insert into " + GeneralConstant.DATABASE_CSV_LOG_FILE + "(action,startDate,endDate,task) values (?,?,?,?)";

            OdbcCommand command = new OdbcCommand(sql, conn);
            OdbcParameter p1 = new OdbcParameter("p1", OdbcType.VarChar);
            p1.Value = action;
            OdbcParameter p2 = new OdbcParameter("p2", OdbcType.VarChar);
            p2.Value = startDate;
            OdbcParameter p3 = new OdbcParameter("p3", OdbcType.VarChar);
            p3.Value = endDate;
            OdbcParameter p4 = new OdbcParameter("p4", OdbcType.VarChar);
            p4.Value = task;
            command.Parameters.Add(p1);
            command.Parameters.Add(p2);
            command.Parameters.Add(p3);
            command.Parameters.Add(p4);
            command.ExecuteNonQuery();
            command.Dispose();
            conn.Close();
        }
        public DataTable readLog()
        {
            OdbcConnection conn = new OdbcConnection(DATABASE_CONN_STRING);
            conn.Open();
            OdbcDataAdapter dataAdapter = new OdbcDataAdapter("SELECT * FROM [" + GeneralConstant.DATABASE_CSV_LOG_FILE + "]", conn);
            DataTable dt = new DataTable();
            dataAdapter.Fill(dt);
            dataAdapter.Dispose();
            conn.Close();
            return dt;
        }
        public void deleteLog()
        {
            string filePath = GeneralConstant.DATABASE_FOLDER + GeneralConstant.DATABASE_CSV_LOG_FILE;
            List<string> lines = File.ReadAllLines(filePath).ToList();
            File.WriteAllLines(filePath, lines.GetRange(0, lines.Count - 1));
        }
        #endregion
    }
}
